#ifndef __M_ROOM_H__
#define __M_ROOM_H__
#include "util.hpp"
#include "log.hpp"
#include "online.hpp"
#include "db.hpp"

#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2
typedef enum {GAME_START, GAME_OVER}room_state;
class room
{
public:
    room(uint64_t room_id, user_table *tb_user, online_manager *online_user)
        : _room_id(room_id)
        , _state(GAME_START)
        , _player_count(0)
        , _tb_user(tb_user)
        , _online_user(online_user)
        , _board(BOARD_ROW, std::vector<int>(BOARD_COL, 0))
        {
            DLOG("%lu 房间创建成功!!", _room_id);
        }

    ~room()
    {
        DLOG("%lu 房间销毁成功!!", _room_id);
    }

    uint64_t id() { return _room_id; }
    room_state state() { return _state; }
    int player_count() { return _player_count; }
    void add_white_user(uint64_t uid) 
    {
        _white_id = uid;
        _player_count++;
    }
    void add_black_user(uint64_t uid)
    {
        _black_id = uid;
        _player_count++;
    }
    uint64_t get_white_user() { return _white_id; }
    uint64_t get_black_user() { return _black_id; }

    //处理下棋动作
    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value json_resp = req;
        int chess_row = req["row"].asInt();//下棋的位置
        int chess_col = req["col"].asInt();//下棋的位置
        uint64_t cur_uid = req["uid"].asUInt64();//谁在下棋

        //判断双方是否都在线，若一方不在线，另一方直接胜利
        if (_online_user->is_in_game_room(_white_id) == false)
        {
            json_resp["result"] = true;
            json_resp["reason"] = "运气真好！对方掉线，不战而胜！";
            json_resp["winner"] = (Json::UInt64)_black_id;
            return json_resp;
        }
        if (_online_user->is_in_game_room(_black_id) == false)
        {
            json_resp["result"] = true;
            json_resp["reason"] = "运气真好！对方掉线，不战而胜！";
            json_resp["winner"] = (Json::UInt64)_white_id;
            return json_resp;
        }

        //判断下棋的位置是否合理
        if (_board[chess_row][chess_col] != 0)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "当前位置已经有了其他棋子！";
            return json_resp;
        }
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
        _board[chess_row][chess_col] = cur_color;

        //判断是否有玩家胜利
        uint64_t winner_id = check_win(chess_row, chess_col, cur_color);
        if (winner_id != 0)
        {
            json_resp["reason"] = "五星连珠，战无敌！";
        }
        json_resp["result"] = true;
        json_resp["winner"] = (Json::UInt64)winner_id;
        return json_resp;
    }

    //处理聊天
    Json::Value handle_chat(Json::Value &req)
    {
        Json::Value json_resp = req;
        //检测消息中是否包含敏感词
        std::string msg = req["message"].asString();
        size_t pos = msg.find("垃圾");
        if (pos != std::string::npos)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "消息中包含敏感词汇，不能发送！";
            return json_resp;
        }
        json_resp["result"] = true;
        return json_resp;
    }

    //将指定信息广播给房间中的所有玩家
    void broadcast(Json::Value &rsp)
    {
        //对要发送的消息进行序列化
        std::string body;
        json_util::serialize(rsp, body);
        //获取房间中所有用户的通信连接
        //发送响应信息
        wsserver_t::connection_ptr wconn = _online_user->get_conn_from_room(_white_id);
        //get()方法返回管理对象的原始指针
        if (wconn.get() != nullptr)
        {
            wconn->send(body);
        }
        else DLOG("房间-白棋玩家连接获取失败！");

        wsserver_t::connection_ptr bconn = _online_user->get_conn_from_room(_black_id);
        if (bconn.get() != nullptr)
        {
            bconn->send(body);
        }
        else DLOG("房间-黑棋玩家连接获取失败！");

        return;
    }

    //处理玩家退出房间动作
    void handle_exit(uint64_t uid)
    {
        Json::Value json_resp;
        if (_state == GAME_START)
        {
            uint64_t winner_id = (uid == _white_id ? _black_id : _white_id);
            json_resp["optype"] = "put_chess";
            json_resp["result"] = true;
            json_resp["reason"] = "对方掉线，不战而胜！";
            json_resp["room_id"] = (Json::UInt64)_room_id;
            json_resp["uid"] = (Json::UInt64)uid;
            json_resp["row"] = -1;
            json_resp["col"] = -1;
            json_resp["winner"] = (Json::UInt64)winner_id;
            uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
            _tb_user->win(winner_id);
            _tb_user->lose(loser_id);
            _state = GAME_OVER;
            broadcast(json_resp);
        }
        _player_count--;
        return;
    }

    //总的请求处理函数，在函数内部，区分请求类型，根据不同的请求调用不同的处理函数，得到响应进行广播
    void handle_request(Json::Value &req)
    {
        Json::Value json_resp;
        //校验房间号是否匹配
        uint64_t room_id = req["room_id"].asUInt64();
        if (room_id != _room_id)
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "房间号不匹配！";
            return broadcast(json_resp);
        }

        //根据不同的请求类型调用不同的处理函数
        if (req["optype"].asString() == "put_chess")
        {
            json_resp = handle_chess(req);
            if (json_resp["winner"].asUInt64() != 0)
            {
                uint64_t winner_id = json_resp["winner"].asUInt64();
                uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _state = GAME_OVER;
            }
        }
        else if (req["optype"].asString() == "chat")
        {
            json_resp = handle_chat(req);
        }
        else
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "未知请求类型！";
        }
        std::string body;
        json_util::serialize(json_resp, body);
        DLOG("房间-广播动作：%s", body.c_str());
        return broadcast(json_resp);
    }

private:
    //检测是否有连续相同颜色的五颗棋子
    bool five(int row, int col, int row_offset, int col_offset, int color)
    {

        int count = 1;//记录同色棋子的数量
        int search_row = row + row_offset;
        int search_col = col + col_offset;
        //从下棋位置开始，正向检索
        while (search_row >= 0 && search_row < BOARD_ROW && 
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            count++;
            search_row += row_offset;
            search_col += col_offset;
        }

        //返回到下棋的位置
        search_row = row - row_offset;
        search_col = col - col_offset;
        //从下棋位置开始，反向检索
        while (search_row >= 0 && search_row < BOARD_ROW && 
               search_col >= 0 && search_col < BOARD_COL &&
               _board[search_row][search_col] == color)
        {
            count++;
            search_row -= row_offset;
            search_col -= col_offset;
        }

        return (count >= 5);
    }

    uint64_t check_win(int row, int col, int color)
    {
        if (five(row, col, 0, 1, color)//横向移动
            || five(row, col, 1, 0, color)//纵向移动
            || five(row, col, -1, 1, color)//沿着副对角线移动
            || five(row, col, -1, -1, color))//沿着主对角线移动
        {
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
        return 0;
    }

private:
    uint64_t _room_id;
    room_state _state;
    int _player_count;
    uint64_t _white_id;
    uint64_t _black_id;
    user_table *_tb_user;
    online_manager *_online_user;
    std::vector<std::vector<int>> _board;
};


using room_ptr = std::shared_ptr<room>;

class room_manager
{
public:
    //初始化房间id计数器
    room_manager(user_table *tb_user, online_manager *online_user)
                : _next_rid(1), _tb_user(tb_user), _online_user(online_user)
                {
                    DLOG("房间管理模块初始化完毕！");
                }
    
    ~room_manager() { DLOG("房间管理模块即将销毁！"); }

    //为两个用户创建房间，并返回房间的智能指针管理对象
    room_ptr create_room(uint64_t uid1, uint64_t uid2)
    {
        //校验两个用户是否都在游戏大厅中
        if (_online_user->is_in_game_hall(uid1) == false)
        {
            DLOG("用户：%lu 不在大厅中，创建房间失败！", uid1);
            return room_ptr();
        }
        if (_online_user->is_in_game_hall(uid2) == false)
        {
            DLOG("用户：%lu 不在大厅中，创建房间失败！", uid2);
            return room_ptr();
        }

        //创建房间，将用户添加到房间中
        std::unique_lock<std::mutex> lock(_mutex);
        room_ptr rp(new room(_next_rid, _tb_user, _online_user));
        rp->add_white_user(uid1);
        rp->add_black_user(uid2);

        //管理房间信息
        _rooms.insert(std::make_pair(_next_rid, rp));
        _users.insert(std::make_pair(uid1, _next_rid));
        _users.insert(std::make_pair(uid2, _next_rid));
        _next_rid++;

        return rp;
    }

    //通过房间id获取房间信息
    room_ptr get_room_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _rooms.find(rid);
        if (it == _rooms.end())
        {
            return room_ptr();
        }
        return it->second;
    }

    //通过用户id获取房间信息
    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto uit = _users.find(uid);
        if (uit == _users.end())
        {
            return room_ptr();
        }
        uint64_t rid = uit->second;
        auto rit = _rooms.find(rid);
        if (rit == _rooms.end())
        {
            return room_ptr();
        }
        return rit->second;
    }

    //通过房间id销毁房间
    void remove_room(uint64_t rid)
    {
        //由于房间信息是交给share_ptr来管理的，因此当share_ptr的计数器==0，就会自动释放
        //下面的操作都是减少对房间对象的引用
        room_ptr rp = get_room_by_rid(rid);
        if (rp.get() == nullptr)
        {
            return;
        }

        uint64_t uid1 = rp->get_white_user();
        uint64_t uid2 = rp->get_black_user();

        std::unique_lock<std::mutex> lock(_mutex);
        _users.erase(uid1);
        _users.erase(uid2);

        _rooms.erase(rid);
    }

    //删除房间中指定的用户，若没有用户则销毁房间
    void remove_room_user(uint64_t uid)
    {
        room_ptr rp = get_room_by_uid(uid);
        if (rp.get() == nullptr)
        {
            return;
        }
        //处理房间中玩家的退出动作
        rp->handle_exit(uid);

        //检测房间中是否还有其他用户
        if(rp->player_count() == 0)
        {
            remove_room(rp->id());
        }
        
        return;
    }

private:
    uint64_t _next_rid;
    std::mutex _mutex;
    user_table *_tb_user;
    online_manager *_online_user;
    std::unordered_map<uint64_t, room_ptr> _rooms;
    std::unordered_map<uint64_t, uint64_t> _users;
};

#endif